package com.opennews.openplatform

import grails.compiler.GrailsCompileStatic
import grails.gorm.transactions.Transactional
import groovy.transform.CompileDynamic
import org.hibernate.criterion.CriteriaSpecification
import org.springframework.beans.factory.annotation.Value

@Transactional(readOnly = true)
@GrailsCompileStatic
class AppReleaseLogService {
    @Value('${openplatform.resource.httpBaseUrl}')
    String httpBaseUrl

    @Value('${openplatform.resource.apkDirectoryName}')
    String apkDirectoryName

    /**
     * Prepares the initial data for AppReleaseLog.
     */
    @Transactional
    void init() {
        if (AppReleaseLog.count() == 0) {
            new AppReleaseLog().with {
                // Month value is 0-based. e.g., 0 for January.
                // The releaseDate is 2019-12-31.
                releaseDate = new GregorianCalendar(2019, 11, 31).getTime()
                appVersion = '1.0.0'
                changeLog = '初始版本'
                apkFilename = "family-expense.apk"
                appStoreUrl = "https://"
                save()
            }
        }
    }

    /**
     * Queries the app release details by its version.
     * @param appVersion: The string of the app version.
     * @return The map instance which contains keys below.
     *          releaseDate: Date when the app released.
     *          appVersion: String of the app version.
     *          apkFilename: String of the apk filename.
     *          apkUrl: String of apk url for android.
     *          isMandatory: If this is a mandatory update.
     */
    @CompileDynamic
    Map<String, ?> queryByAppVersion(String appVersion) {
        def result = AppReleaseLog.createCriteria().get {
            // Transforms the result data format to Map instance.
            resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)

            // Where a property equals a particular value.
            eq("appVersion", appVersion)

            // Specifies the property we need.
            projections {
                property("releaseDate", "releaseDate")
                property("appVersion", "appVersion")
                property("apkFilename", "apkFilename")
                property("isMandatory", "isMandatory")
            }
        } as Map<String, ?>

        if (result != null) {
            result.apkUrl = "$httpBaseUrl/$apkDirectoryName/${result.apkFilename}"
        }

        return result
    }

    /**
     *
     * Queries the latest version which released after the provided releaseDate.
     * @param appVersion: The string of the app version.
     * @return The map instance which contains keys below.
     *          releaseDate: Date when the app released.
     *          appVersion: String of the app version.
     *          changeLog: String of the release change log.
     *          apkFilename: String of the apk filename.
     *          apkUrl: String of apk url for android.
     *          appStoreUrl: String of app store url for ios.
     *          isMandatory: If this is a mandatory update.
     */
    @CompileDynamic
    Map<String, ?> queryLatestVersion(String appVersion) {
        // Different database environment causes different date type value.
        // For example, the value in database is '2019-12-31 00:00:00' but the retrieved value
        // might be '2019-12-31 14:00:00'. It might caused by time zone setting.
        // That's why using sub-query in where instead of using the input releaseDate as parameter.
        def hql = """
                    SELECT
                        releaseDate,
                        appVersion,
                        changeLog,
                        apkFilename,
                        appStoreUrl,
                        (
                            SELECT
                                COUNT(id)
                            FROM
                                AppReleaseLog
                            WHERE
                                releaseDate > (
                                    SELECT 
                                        releaseDate 
                                    FROM 
                                        AppReleaseLog 
                                    WHERE 
                                        appVersion = :appVersion
                                ) AND
                                isMandatory = true
                        ) > 0 AS isMandatory
                    FROM
                        AppReleaseLog
                    WHERE
                        releaseDate > (
                            SELECT
                                releaseDate
                            FROM
                                AppReleaseLog
                            WHERE
                                appVersion = :appVersion
                        )
                    ORDER BY
                        releaseDate DESC
                  """

        def list = AppReleaseLog.executeQuery(hql, [appVersion: appVersion], [max: 1]).collect {[
            releaseDate: it[0],
            appVersion: it[1],
            changeLog: it[2],
            apkFilename: it[3],
            apkUrl: "$httpBaseUrl/$apkDirectoryName/${it[3]}",
            appStoreUrl: it[4],
            isMandatory: it[5]
        ]}

        return list.size() > 0 ? list.first() : null
    }

    /**
     * Lists change log info.
     * @param pageSize: How many records is return in one trip.
     * @param pageIndex: Where to take rows.
     * @return The list of map instance which contains keys below.
     *          id: String of the app release log id.
     *          releaseDate: Date of this version released.
     *          appVersion: The string of this app version.
     *          changeLog: The string of this change log info.
     *          isMandatory: If this is a mandatory update.
     */
    @CompileDynamic
    List<Map<String, ?>> listChangeLog(Integer pageSize, Integer pageIndex) {
        return AppReleaseLog.createCriteria().list {
            // Transforms the result data format to Map instance.
            resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)

            projections {
                property("id", "id")
                property("releaseDate", "releaseDate")
                property("appVersion", "appVersion")
                property("changeLog", "changeLog")
                property("isMandatory", "isMandatory")
            }

            order("releaseDate", "desc")

            // How many rows are return for this trip.
            maxResults(pageSize)

            // Where to take the rows.
            firstResult(pageSize * pageIndex)
        } as List<Map<String, ?>>
    }
}